#pragma once
using namespace std;
#include<iostream>

namespace bit
{
	template<class T>
	class auto_ptr
	{
		T* _ptr = nullptr;
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		auto_ptr(auto_ptr<T>& p)
		{
			_ptr = p._ptr;
			p._ptr = nullptr;
		}

		auto_ptr<T>& operator=(const auto_ptr<T>& p)
		{
			if (_ptr != p._ptr)
			{
				if (_ptr)
					delete _ptr;
				_ptr = p._ptr;
				p._ptr = nullptr;
			}
			return *this;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}
	};

	template<class T>
	class unique_ptr
	{
		T* _ptr = nullptr;
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		unique_ptr(unique_ptr<T>& p) =delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& p) = delete;
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

	};

	template<class T>
	class shared_ptr
	{
		T* _ptr = nullptr;
		int* _pcount = 0;
	public:
		shared_ptr(T* ptr)
			:_ptr(ptr)
			,_pcount(new int(1))
		{		}

		shared_ptr(shared_ptr<T>& p)
		{
			_pcount = p._pcount;
			_ptr = p._ptr;
			(*_pcount)++;
		}

		void release()
		{
			if (--(*_pcount) == 0 && _ptr)
			{
				delete _ptr;
				delete _pcount;
			}
		}

		shared_ptr<T>& operator=(const shared_ptr<T>& p)
		{
			if (_ptr != p._ptr)
			{
				release();
				_ptr = p._ptr;
				_pcount = p._pcount;
			}
			return *this;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		~shared_ptr()
		{
			release();
		}
	};
}
